from itsdangerous import SignatureExpired
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

from common.signature import SignatureTools
from license import constants as c
from license.models.account import Account, Reseller
from license.models.app import APPModel
from project import settings

# 此代码为common自带
"""
class AccountAuthentications(BaseAuthentication):

    def authenticate(self, request):
        # 获取TOKEN
        auth_token = request.META.get('HTTP_X_TOKEN', None)
        if not auth_token:
            raise AuthenticationFailed("TOKEN不能为空:{}".format(c.ERROR_TOKEN_EMPTY))
        # 实例化TimedJSONWebSignatureSerializer对象,准备解密token
        s = Serializer(
            secret_key=settings.SECRET_KEY,
            algorithm_name="HS256"
        )
        try:
            # 解密token,如果token过期,会抛出异常,要抓
            # 解密出来的token可以访问payload中的数据
            decode_token = s.loads(auth_token)
            # 用户可能不存在,要抓异常
            account = models.Account.objects.get(
                Q(mobile=decode_token['mobile']) & Q(name=decode_token['name']) & Q(is_deleted=c.BOOL_TYPE_FALSE)
            )
            # 返回用户对象和原始token
            return account, auth_token
        except models.Account.DoesNotExist:
            raise AuthenticationFailed("未找到令牌对应的账号:{}".format(c.ERROR_TOKEN_NOT_FOUND_ACCOUNT))
        except SignatureExpired:
            raise AuthenticationFailed("令牌过期请重新登录:{}".format(c.ERROR_TOKEN_EXPIRED))
        except Exception:
            raise AuthenticationFailed("令牌发生未知错误:{}".format(c.ERROR_TOKEN_ERROR))
"""


class SignatureAuthentication(BaseAuthentication):
    """
    签名认证
    """

    def authenticate(self, request):
        app_id = request.META.get('HTTP_APPID', None)
        timestamp = request.META.get('HTTP_TIMESTAMP', None)
        nonce = request.META.get('HTTP_NONCE', None)
        signature = request.META.get('HTTP_SIGNATURE', None)
        if not (app_id and timestamp and nonce and signature):
            raise AuthenticationFailed("缺少签名参数:{}".format(c.ERROR_AUTHENTICATION_PARAM))
        # 获取请求参数
        payload = {}
        # 区分是get还是post请求
        if request.method == 'POST':
            payload = request.data
        elif request.method == 'GET':
            payload = request.query_params
        # 判断请求是否超时
        if not SignatureTools.is_expired_time(int(timestamp)):
            raise AuthenticationFailed("签名时间戳已过期:{}".format(c.ERROR_AUTHENTICATION_TIMESTAMP_EXPIRE))
        # 判断appid是否合法
        try:
            app = APPModel.objects.get(app_id=app_id)
        except APPModel.DoesNotExist:
            raise AuthenticationFailed("无效appid:{}".format(c.ERROR_AUTHENTICATION_APPID_INVALID))
        # 验签
        if signature != SignatureTools.get_sign_true(app_id, app.app_secret, nonce, timestamp, payload):
            raise AuthenticationFailed("签名错误:{}".format(c.ERROR_AUTHENTICATION_SIGNATURE_INVALID))
        # 通过本认证类
        return None


class TokenAuthentication(BaseAuthentication):
    """
    Token认证
    """

    def authenticate(self, request):
        # 获取请求头的token
        user_token = request.META.get('HTTP_USER_TOKEN_HEADER', None)
        cert_token = request.META.get('HTTP_CERT_TOKEN_HEADER', None)
        if not (user_token or cert_token):
            raise AuthenticationFailed("token不能为空:{}".format(c.ERROR_AUTHENTICATION_TOKEN_EMPTY))
        # 实例化TimedJSONWebSignatureSerializer对象, 准备解密
        s = Serializer(
            secret_key=settings.JWT_SECRET_KEY,
            algorithm_name='HS256'
        )
        # 如果传了user_token, 优先使用user_token
        try:
            if user_token:
                decode_token = s.loads(user_token)
            else:
                # 否则, 使用cert_token
                decode_token = s.loads(cert_token)
            # 用解密出来的id, 检索数据
            # 这里使用filter避免抛出异常
            account = Account.objects.filter(id=decode_token['id'])
            reseller = Reseller.objects.filter(id=decode_token['id'])
            if not (account or reseller):
                raise AuthenticationFailed("未找到令牌对应账号:{}".format(c.ERROR_AUTHENTICATION_ACCOUNT_NOT_FOUND))
            # 优先返回account
            if account:
                return account[0], decode_token['id']
            return reseller[0], decode_token['id']
        except SignatureExpired:
            raise AuthenticationFailed("令牌过期请重新登陆:{}".format(c.ERROR_AUTHENTICATION_TOKEN_EXPIRED))
        except Exception as e:
            print(e)
            raise AuthenticationFailed("令牌发生未知错误:{}".format(c.ERROR_AUTHENTICATION_TOKEN_ERROR))
